package com.aliyun.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alipay.v3.ApiClient;
import com.alipay.v3.ApiException;
import com.alipay.v3.api.AlipayTradeApi;
import com.alipay.v3.api.AlipayTradeFastpayRefundApi;
import com.alipay.v3.model.*;
import com.alipay.v3.util.GenericExecuteApi;
import com.aliyun.enums.AliyunPayMethod;
import com.aliyun.handler.AliyunException;
import com.aliyun.pojo.pay.AliyunPay;
import com.aliyun.pojo.pay.AliyunPayRefundParam;
import com.aliyun.pojo.pay.AliyunPayTradeParam;
import com.aliyun.properties.AliyunProperties;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * 阿里云支付服务
 */
@Slf4j
public class AliyunPayService implements InitializingBean {

    @Resource
    AliyunProperties aliyunProperties;

    @Resource
    ApiClient apiClient;

    AliyunPay aliyunPay;

    /**
     * @param out_trade_no 订单号
     * @param total_amount 订单总金额，单位为元，精确到小数点后两位，
     * @param subject      商品的标题/交易标题/订单标题/订单关键字等。
     * @param goodsDetails  商品信息
     * @return 完整 form 表单的 html（包含自动提交脚本）
     */
    public String payment(String out_trade_no, BigDecimal total_amount, String subject, GoodsDetail[] goodsDetails) {
        GenericExecuteApi api = new GenericExecuteApi(apiClient);
        // 构造请求参数以调用接口
        Map<String, Object> bizParams = new HashMap<>();
        AliyunPayTradeParam param = new AliyunPayTradeParam(out_trade_no, total_amount, subject);
        param.setGoods_detail(goodsDetails);
        Map<String, Object> bizContent = BeanUtil.beanToMap(param);
        bizParams.put("biz_content", bizContent);
        bizParams.put("return_url", aliyunPay.getReturn_url());
        bizParams.put("notify_url", aliyunPay.getNotify_url());
        try {
            return api.pageExecute(AliyunPayMethod.ALIPAY_TRADE_PAGE_PAY, "POST", bizParams);
        } catch (ApiException e) {
            log.error("统一下单并支付异常：{}", e.getMessage());
            throw new AliyunException(e.getMessage());
        }
    }


    /**
     * 该接口提供所有支付宝支付订单的查询，商户可以通过该接口主动查询订单状态，完成下一步的业务逻辑。
     *
     * @param out_trade_no 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
     * @param trade_no     支付宝交易号，和商户订单号不能同时为空
     */
    public AlipayTradeQueryResponseModel queryTrade(String out_trade_no, String trade_no) {
        if (out_trade_no == null && trade_no == null) {
            throw new AliyunException("商户订单号和支付宝交易号不能同时为空");
        }
        // 构造请求参数以调用接口
        AlipayTradeApi api = new AlipayTradeApi(apiClient);
        AlipayTradeQueryModel data = new AlipayTradeQueryModel();
        // 设置订单支付时传入的商户订单号
        data.setOutTradeNo(out_trade_no);
        // 设置支付宝交易号
        data.setTradeNo(trade_no);
        try {
            return api.query(data);
        } catch (ApiException e) {
            AlipayTradeQueryDefaultResponse errorObject = (AlipayTradeQueryDefaultResponse) e.getErrorObject();
            log.error("交易查询失败 订单号：{} 失败原因：{}",out_trade_no,errorObject.getAlipayTradeQueryErrorResponseModel().getMessage());
        }
        return null;
    }

    /**
     * 用于交易创建后，用户在一定时间内未进行支付，可调用该接口直接将未付款的交易进行关闭。
     *
     * @param out_trade_no 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 trade_no,out_trade_no如果同时存在优先取trade_no
     * @param trade_no     该交易在支付宝系统中的交易流水号。最短 16 位，最长 64 位。和out_trade_no不能同时为空，如果同时传了 out_trade_no和 trade_no，则以 trade_no为准。
     * @return
     */
    public AlipayTradeCloseResponseModel close(String out_trade_no, String trade_no) {
        // 构造请求参数以调用接口
        AlipayTradeApi api = new AlipayTradeApi(apiClient);
        AlipayTradeCloseModel data = new AlipayTradeCloseModel();
        // 设置该交易在支付宝系统中的交易流水号
        data.setTradeNo(trade_no);
        // 设置订单支付时传入的商户订单号
        data.setOutTradeNo(out_trade_no);
        try {
            return api.close(data);
        } catch (ApiException e) {
            AlipayTradeCloseDefaultResponse errorObject = (AlipayTradeCloseDefaultResponse) e.getErrorObject();
            log.error("关闭订单失败 订单号：{} 失败原因：{}",out_trade_no,errorObject.getAlipayTradeCloseErrorResponseModel().getMessage());
        }
        return null;
    }

    public AlipayTradeRefundResponseModel refund(AliyunPayRefundParam aliyunPayRefundParam) {
        // 构造请求参数以调用接口
        AlipayTradeApi api = new AlipayTradeApi(apiClient);
        AlipayTradeRefundModel data = new AlipayTradeRefundModel();
        // 设置商户订单号
        data.setOutTradeNo(aliyunPayRefundParam.getOut_trade_no());
        // 设置支付宝交易号
        data.setTradeNo(aliyunPayRefundParam.getTrade_no());
        // 设置退款金额
        data.setRefundAmount(aliyunPayRefundParam.getRefund_amount().toString());
        // 设置退款原因说明
        data.setRefundReason(aliyunPayRefundParam.getRefund_reason());
        // 设置退款请求号
        data.setOutRequestNo(aliyunPayRefundParam.getOut_request_no());

        try {
            return api.refund(data);
        } catch (ApiException e) {
            AlipayTradeRefundDefaultResponse errorObject = (AlipayTradeRefundDefaultResponse) e.getErrorObject();
            log.error("订单退款失败 订单号：{} 失败原因：{}",aliyunPayRefundParam.getOut_trade_no(),errorObject.getAlipayTradeRefundErrorResponseModel().getMessage());
            return null;
        }
    }

    /**
     * 商户可使用该接口查询自已通过alipay.trade.refund提交的退款请求是否执行成功。
     *
     * @param out_request_no 退款请求号。 请求退款接口时，传入的退款请求号，如果在退款请求时未传入，则该值为创建交易时的商户订单号。
     * @param out_trade_no   商户订单号。 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 trade_no,out_trade_no如果同时存在优先取trade_no
     * @param trade_no       支付宝交易号。 和商户订单号不能同时为空
     * @return
     */
    public AlipayTradeFastpayRefundQueryResponseModel queryRefund(String out_request_no, String out_trade_no, String trade_no) {
        // 构造请求参数以调用接口
        AlipayTradeFastpayRefundApi api = new AlipayTradeFastpayRefundApi(apiClient);
        AlipayTradeFastpayRefundQueryModel data = new AlipayTradeFastpayRefundQueryModel();
        // 设置支付宝交易号
        data.setTradeNo(trade_no);
        // 设置商户订单号
        data.setOutTradeNo(out_trade_no);
        // 设置退款请求号
        data.setOutRequestNo(out_request_no);
        try {
            return api.query(data);
        } catch (ApiException e) {
            AlipayTradeFastpayRefundQueryDefaultResponse errorObject = (AlipayTradeFastpayRefundQueryDefaultResponse) e.getErrorObject();
            log.error("退款查询失败 订单号：{} 失败原因：{}",out_request_no,errorObject.getAlipayTradeFastpayRefundQueryErrorResponseModel().getMessage());
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        aliyunPay = aliyunProperties.getPay();
    }
}
